/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "iconprovider.h"
#include <QFile>
#include <QMutexLocker>
#include <QIcon>
#include <QDir>
#include <QProcess>
#include <QDebug>
#include <QThread>
#include <QDomDocument>
#include "icontheme.h"

QMutex qiconMutex;

Q_GLOBAL_STATIC_WITH_ARGS(QStringList, appIconPaths, ({
    "/usr/share/icons/hicolor",
    "/usr/share/icons/gnome",
    "/usr/share/pixmaps",
    "/usr/local/share/icons",
    qEnvironmentVariable("HOME") + "/.local/share/icons"
}));

Q_GLOBAL_STATIC_WITH_ARGS(QStringList, appIconFormats, ({
    "*.svg", "*.png", "*.xpm", "*.ico"
}));

Q_GLOBAL_STATIC_WITH_ARGS(QStringList, appIconSize, ({
    "scalable", "128x128", "96x96",
}));

static QString lookForLocalIconFileForIconId(const QString &iconName)
{
    for (const auto &iconPath : qAsConst(*appIconPaths)) {
        QDir dir(iconPath);
        auto icons = dir.entryList(*appIconFormats);
        for (const auto &icon : qAsConst(icons)) {
            if (icon.startsWith(iconName)) {
                return "file#" + iconPath + "/" + icon;
            }
        }
        auto dirList = dir.entryInfoList(*appIconSize);
        for (auto &dirName : qAsConst(dirList)) {
            QDir dir2(dirName.absoluteFilePath());
            auto dir2List = dir2.entryInfoList(QStringList() << "apps" << "mimetypes");
            for (const auto &path : qAsConst(dir2List)) {
                QDir iconDir(path.absoluteFilePath());
                auto icons = iconDir.entryList(*appIconFormats);
                for (const auto &icon : qAsConst(icons)) {
                    if (icon.startsWith(iconName)) {
                        return "file#" + path.absoluteFilePath() + "/" + icon;
                    }
                }
            }
        }
    }
    return QString();
}

static bool waitForAndroidAppIconCreated(const QString &iconFile, int waitSecs = 10)
{
    int loopNumber = 0;
    do {
        QFile file(iconFile);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qWarning() << "The android app icon file does not exist." << iconFile;
            return false;
        }
        QDomDocument xml;
        xml.setContent(&file);
        if (!xml.elementsByTagName("image").isEmpty()) {
            return true;
        }
        file.close();
        ++ loopNumber;
        QThread::sleep(1);
    } while(loopNumber > waitSecs);

    return false;
}

static QString doFindIconId(const QString &iconName)
{
    QString _iconName = iconName;
    _iconName.remove(".png");
    _iconName.remove(".svg");
    _iconName.remove(".ico");
    _iconName.remove(".xpm");
    auto *iconTheme = UKUI::IconTheme::getInstance();
    QString theme = iconTheme->iconTheme();
    {
        QMutexLocker locker(&qiconMutex);
        QIcon::setThemeName(theme);
        QIcon icon = QIcon::fromTheme(_iconName);
        if (!icon.isNull()) {
            return "theme#" + iconTheme->iconTheme() + "#" + _iconName;
        }
    }

    QString iconId = lookForLocalIconFileForIconId(iconName);
    if (!iconId.isEmpty()) {
        return iconId;
    }
    return QString();
}

static QString tryToFindIconId(const QString &iconName)
{
    if (iconName.isEmpty()) {
        return QString();
    }

    if (QFile::exists(iconName)) {
        return "file#" + iconName;
    }

    return doFindIconId(iconName);
}

static QString tryToFindIconIdWithInstallingAndroidApp(const QString &iconName)
{
    if (iconName.isEmpty()) {
        return QString();
    }

    if (QFile::exists(iconName)) {
        if (!waitForAndroidAppIconCreated(iconName)) {
            qWarning() << "Can not load the android app icon." << iconName;
        }
        return "file#" + iconName;
    }
    return doFindIconId(iconName);
}

static QString debianPackageName(const QString &desktopFile)
{
    if (desktopFile.isEmpty()) {
        qWarning() << "Can not query the package info according to the desktop file:" << desktopFile;
        return QString();
    }
    QString pkgInfo;
    QProcess p;
    QString cmd = QString("dpkg -S %1").arg(desktopFile);
    p.start(cmd);
    p.waitForFinished(5000);
    pkgInfo = p.readAll();
    if (pkgInfo.isEmpty()) {
        qWarning() << "Can not query the package info according to the desktop file:" << desktopFile;
        return QString();
    }

    QStringList packageInfos = pkgInfo.split(":");
    if (packageInfos.isEmpty()) {
        qWarning() << "Can not query the package name according to the result of:111";
        return QString();
    }
    return packageInfos.constFirst();
}

bool debianPackageIsInstalled(const QString &pkgName)
{
    if (pkgName.isEmpty()) {
        return false;
    }
    QProcess p;
    QString cmd = QString("dpkg-query --status %1").arg(pkgName);
    p.start(cmd);
    p.waitForFinished(5000);
    QString pkgInfo = p.readAll();
    if (pkgInfo.isEmpty()) {
        qWarning() << "Can not query the package info:" << pkgName;
        return false;
    }
    QStringList pkgDetails = pkgInfo.split('\n');
    if (pkgDetails.isEmpty()) {
        qWarning() << "Can not query the package details info:" << pkgInfo;
        return false;
    }
    qWarning() << "details " << pkgDetails;
    for (auto &detail : qAsConst(pkgDetails)) {
        if (detail.startsWith("Status:") && detail.contains("install ok")) {
            return true;
        }
    }
    return false;
}

static bool waitForAppInstalledComplete(const QString &desktopFile, int waitSecs = 10)
{
    QString pkgName = debianPackageName(desktopFile);
    int checkCounter = 0;
    for (;
         (pkgName.isEmpty() || !debianPackageIsInstalled(pkgName)) && checkCounter < 10;
         ++ checkCounter) {
        if (pkgName.isEmpty()) {
            pkgName = debianPackageName(desktopFile);
        }
        QThread::sleep(1);
    }
    if (checkCounter <= waitSecs) {
        return true;
    }

    return false;
}

static bool waitForAndroidAppInstalledComplete(const QString &iconName, int waitSecs = 5)
{
    int checkCounter = 0;
    for (; !QFile::exists(iconName) && checkCounter <= 5; ++checkCounter) {
        QThread::sleep(1);
    }
    if (checkCounter <= waitSecs) {
        waitForAndroidAppIconCreated(iconName);
    }
    if (checkCounter <= waitSecs) {
        return true;
    }
    return false;
}

static QString defaultIconId()
{
    auto *iconTheme = UKUI::IconTheme::getInstance();
    QString defaultAppIcon = iconTheme->getPathFromName("application-x-desktop", 96);
    return "file#" + defaultAppIcon;
}

IconProvider::IconProvider(QObject *parent)
    : QObject{parent}
{

}

QString IconProvider::iconIdWithExistedApp(const QString &iconName)
{
    QString iconId = tryToFindIconId(iconName);
    if (!iconId.isEmpty()) {
        return iconId;
    }
    return defaultIconId();
}

QString IconProvider::iconIdWithInstallingApp(const QString &iconName, const QString &desktopFile)
{
    QString iconId = tryToFindIconId(iconName);
    if (!iconId.isEmpty()) {
        return iconId;
    }

    if (waitForAppInstalledComplete(desktopFile)) {
        return iconIdWithExistedApp(iconName);
    }
    return defaultIconId();
}

QString IconProvider::iconIdWithInstallingAndroidApp(const QString &iconName)
{
    QString iconId = tryToFindIconIdWithInstallingAndroidApp(iconName);
    if (!iconId.isEmpty()) {
        return iconId;
    }

    if (waitForAndroidAppInstalledComplete(iconName)) {
        return iconIdWithExistedApp(iconName);
    }
    return defaultIconId();
}
